package com.future.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.future.domain.LoginUserDetails;
import com.future.domain.Role;
import com.future.domain.User;
import com.future.exception.AccountDisabledException;
import com.future.mapper.PermissionMapper;
import com.future.mapper.AdminMapper;
import com.future.mapper.RoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    AdminMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private RoleMapper roleMapper;

    /**
     *
     * 该方法仅在登录时调用一次
     *
     *
     * 由于SecurityConfig配置了@EnableGlobalMethodSecurity(prePostEnabled = true)
     * SpringSecurity会检查Authentication的权限信息，如果为null就403报错
     * 因此返回值LoginUser必须使用携带权限信息的构造函数
     *
     */
    @Override
    public UserDetails loadUserByUsername(String accountOrEmail) throws UsernameNotFoundException {


        /**
         * 根据账户查询 用户
         * loadUserByUsername 是 Spring Security 认证流程的关键步骤，
         * 其核心作用是获取 “最新、最权威的用户信息”
         * （包括账号状态、密码、权限等），用于验证用户身份。
         *
         * -->Redis 更适合存储 “已认证的用户信息”
         * （如登录后生成的 token 对应的用户数据），而非 “待认证的原始用户信息”。
         *
         * 但是只要查直接查数据库了必然会有性能问题。
         * 若用户量极大，数据库查询成为瓶颈，
         * 可在 loadUserByUsername 中先查 Redis 缓存，缓存未命中再查数据库
         *
         *
         * 综上所述 查mysql
         */

        //查询数据库
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getAccount, accountOrEmail)
                .or()
                .eq(User::getEmail, accountOrEmail);
        User user = userMapper.selectOne(qw);
        if (user.getStatus()==3){
            throw new AccountDisabledException("禁用注销账号");
        }
        //没有查到用户就抛出异常
        if(Objects.isNull(user)){
            throw new RuntimeException();
        }

        // 查询用户的权限信息
        List<String> permissions = permissionMapper.selectPermsByUserId(user.getId());
        List<Role> roles = roleMapper.selectUserRolesByUserId(user.getId());
        //把数据封装成UserDetails类型返回
        // 1. 将角色列表转换为角色名称字符串列表
        List<String> roleNames = roles.stream()
                .map(Role::getRoleName)
                .collect(Collectors.toList());

        // 2. 合并权限和角色名称为统一权限列表
        List<String> authorities = new ArrayList<>();
        authorities.addAll(permissions);  // 添加权限字符串
        authorities.addAll(roleNames);    // 添加角色名称字符串
        return new LoginUserDetails(user,authorities);
    }
}

